package me.zhengjie.minbearwcs.serivce.outbound;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhengjie.minbearwcs.dao.agv.inf.AgvMissionDao;
import me.zhengjie.minbearwcs.dao.mission.inf.WcsMissionDao;
import me.zhengjie.minbearwcs.dao.outbound.inf.*;
import me.zhengjie.minbearwcs.dao.stock.inf.MaterialStockDao;
import me.zhengjie.minbearwcs.dao.stock.inf.PalletDao;
import me.zhengjie.minbearwcs.dao.storage.inf.LargeStorageDao;
import me.zhengjie.minbearwcs.dao.storage.inf.MediumStorageDao;
import me.zhengjie.minbearwcs.dao.storage.inf.SmallStorageDao;
import me.zhengjie.minbearwcs.dao.storage.inf.StorageDao;
import me.zhengjie.minbearwcs.entity.DTO.outbound.wms.WmsSortException;
import me.zhengjie.minbearwcs.entity.DTO.outbound.wms.order.WmsOutboundOrder;
import me.zhengjie.minbearwcs.entity.DTO.outbound.wms.order.WmsOutboundRequireItem;
import me.zhengjie.minbearwcs.entity.DTO.outbound.wms.order.WmsSuggestStorage;
import me.zhengjie.minbearwcs.entity.DTO.transfer.Tuple;
import me.zhengjie.minbearwcs.entity.PO.agv.AgvMission;
import me.zhengjie.minbearwcs.entity.PO.agv.missionEnum.AgvMissionStatus;
import me.zhengjie.minbearwcs.entity.PO.wcs.mission.WcsMission;
import me.zhengjie.minbearwcs.entity.PO.wcs.mission.status.OutboundSortMissionStatus;
import me.zhengjie.minbearwcs.entity.PO.wcs.mission.type.MissionType;
import me.zhengjie.minbearwcs.entity.PO.wcs.outbound.OutboundOrder;
import me.zhengjie.minbearwcs.entity.PO.wcs.outbound.OutboundOrderDetail;
import me.zhengjie.minbearwcs.entity.PO.wcs.outbound.common.OutboundContentPool;
import me.zhengjie.minbearwcs.entity.PO.wcs.outbound.history.*;
import me.zhengjie.minbearwcs.entity.PO.wcs.outbound.record.OutboundTaskRecord;
import me.zhengjie.minbearwcs.entity.PO.wcs.outbound.sort.SortPalletDetail;
import me.zhengjie.minbearwcs.entity.PO.wcs.outbound.status.OutboundDetailStatus;
import me.zhengjie.minbearwcs.entity.PO.wcs.outbound.status.OutboundOrderStatus;
import me.zhengjie.minbearwcs.entity.PO.wcs.outbound.stockUp.OutboundStockUpRecord;
import me.zhengjie.minbearwcs.entity.PO.wcs.outbound.stockUp.PalletMediumStorageSearchPo;
import me.zhengjie.minbearwcs.entity.PO.wcs.storage.LargeStorage;
import me.zhengjie.minbearwcs.entity.PO.wcs.storage.MediumStorage;
import me.zhengjie.minbearwcs.entity.PO.wcs.storage.SmallStorage;
import me.zhengjie.minbearwcs.entity.PO.wcs.storage.type.StorageType;
import me.zhengjie.minbearwcs.serivce.agv.task.agvTask.AgvTaskDispatcher;
import me.zhengjie.minbearwcs.utils.IdWorker;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;


import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class OutboundService {

    private final OutboundOrderDao outboundOrderDao;
    private final OutboundOrderDetailDao outboundOrderDetailDao;
    private final IdWorker idWorker;
    private final OutboundTaskRecordDao outboundTaskRecordDao;
    private final SmallStorageDao smallStorageDao;
    private final MediumStorageDao mediumStorageDao;
    private final StorageDao storageDao;
    private final PalletDao palletDao;
    private final MaterialStockDao materialStockDao;
    private final SortPalletDetailDao sortPalletDetailDao;
    private final SortPalletDetailHistoryDao sortPalletDetailHistoryDao;
    private final OutboundDao outboundDao;
    private final OutboundTaskRecordHistoryDao outboundTaskRecordHistoryDao;
    private final OutboundStockUpRecordDao outboundStockUpRecordDao;
    private final OutboundStockUpRecordHistoryDao outboundStockUpRecordHistoryDao;
    private final OutboundOrderHistoryDao outboundOrderHistoryDao;
    private final OutboundOrderDetailHistoryDao outboundOrderDetailHistoryDao;
    private final LargeStorageDao largeStorageDao;
    private final WcsMissionDao wcsMissionDao;
    private final AgvMissionDao agvMissionDao;
    private final AgvTaskDispatcher agvTaskDispatcher;

    @Transactional
    public boolean outboundOrderDispatch(WmsOutboundOrder wmsOutboundOrder) {
        OutboundOrder outboundOrder = new OutboundOrder(null, wmsOutboundOrder.getOutboundOrderCode(), wmsOutboundOrder.getLogisticsCompanyCode(),
                OutboundContentPool.OUTBOUND_ORDER_DEFAULT_PRIORITY, OutboundOrderStatus.INIT, LocalDateTime.now(),
                null, null, OutboundContentPool.OUTBOUND_ORDER_NOT_STOCK_UP, wmsOutboundOrder.getOrderType());

        outboundOrderDao.insert(outboundOrder);

        for (WmsOutboundRequireItem wmsOutboundRequireItem : wmsOutboundOrder.getOutboundList()) {
            WmsSuggestStorage suggestStorage = wmsOutboundRequireItem.getSuggestStorage();
            OutboundOrderDetail outboundOrderDetail = new OutboundOrderDetail(null, wmsOutboundOrder.getOutboundOrderCode(), "outbound_detail_" + idWorker.nextId(),
                    wmsOutboundRequireItem.getMaterialCode(), wmsOutboundRequireItem.getOutboundNum(), 0, 0,
                    OutboundDetailStatus.INIT, suggestStorage.getLargeStorage(), suggestStorage.getMediumStorage(), suggestStorage.getSmallStorage());
            outboundOrderDetailDao.insert(outboundOrderDetail);
        }

        return true;
    }


    @Transactional
    public Tuple<String> outboundOrderComplete(String outboundOrderCode) {
        //解锁储位
        List<OutboundTaskRecord> outboundTaskRecordList = outboundTaskRecordDao.selectByOutboundOrderCode(outboundOrderCode);

        // 解锁小储位
        for (OutboundTaskRecord outboundTaskRecord : outboundTaskRecordList) {

            StorageType originStorageType = storageDao.selectStorageTypeBySmallStorage(outboundTaskRecord.getOriginalSmallStorage());
            if ((StorageType.STORAGE_AREA_5F == originStorageType ||
                    StorageType.STORAGE_AREA_6F == originStorageType) &&
                    outboundTaskRecord.getIsSort() == OutboundContentPool.OUTBOUND_RECORD_NOT_SORT
            ) {
                smallStorageDao.updateOutboundFreezeBySmallStorageCode(
                        outboundTaskRecord.getOriginalSmallStorage(),
                        null
                );
            }


            // 清除中储位标记
            judgeAndClearMediumStorage(outboundTaskRecord.getOriginalSmallStorage());

            // 清除物料信息以及栈板信息
            if (outboundTaskRecord.getIsSort() == OutboundContentPool.OUTBOUND_RECORD_NOT_SORT) {
                materialStockDao.deleteByPallet(outboundTaskRecord.getPalletCode());
                palletDao.deleteByPalletCode(outboundTaskRecord.getPalletCode());
            }


            // 处理record到历史表
            outboundTaskRecordHistoryDao.insert(
                    new OutboundTaskRecordHistory(null, outboundTaskRecord.getOutboundRecordId(), outboundTaskRecord.getOutboundOrderCode(),
                            outboundTaskRecord.getOutboundOrderDetailId(), outboundTaskRecord.getPalletCode(), outboundTaskRecord.getMaterialCode(),
                            outboundTaskRecord.getMaterialNum(), outboundTaskRecord.getStartFloorNum(), outboundTaskRecord.getStartSmallStorage(),
                            outboundTaskRecord.getDestinationFloorNum(), outboundTaskRecord.getDestinationSmallStorage(), outboundTaskRecord.getIsSort(),
                            outboundTaskRecord.getOriginalPalletMaterialNum(), outboundTaskRecord.getCreateTime(), outboundTaskRecord.getOriginalSmallStorage(),
                            outboundTaskRecord.getLogisticsCompanyCode())
            );

            outboundTaskRecordDao.deleteById(outboundTaskRecord.getId());
        }

        // 备货record处理
        List<OutboundStockUpRecord> outboundStockUpRecordList = outboundStockUpRecordDao.selectByOutboundOrderCode(outboundOrderCode);
        for (OutboundStockUpRecord outboundStockUpRecord : outboundStockUpRecordList) {
            outboundStockUpRecordHistoryDao.insert(
                    new OutboundStockUpRecordHistory(null, outboundStockUpRecord.getStockUpId(), outboundStockUpRecord.getOutboundOrderCode(), outboundStockUpRecord.getOutboundOrderDetailId(),
                            outboundStockUpRecord.getPalletCode(), outboundStockUpRecord.getMaterialCode(), outboundStockUpRecord.getMaterialNum(), outboundStockUpRecord.getStartFloorNum(),
                            outboundStockUpRecord.getStartSmallStorage(), outboundStockUpRecord.getDestinationFloorNum(), outboundStockUpRecord.getDestinationSmallStorage(), outboundStockUpRecord.getCreateTime())
            );
            outboundStockUpRecordDao.deleteById(outboundStockUpRecord.getId());
        }


        // 处理分拣的板
        List<SortPalletDetail> sortPalletDetailList = sortPalletDetailDao.selectByOutboundOrderCode(outboundOrderCode);
        for (SortPalletDetail sortPalletDetail : sortPalletDetailList) {
            Integer processNum = outboundDao.selectFakerPalletHasProcessOrderNum(sortPalletDetail.getPalletCode());
            // 最后一单绑定的
            if (processNum <= 1) {
                materialStockDao.deleteByPallet(sortPalletDetail.getPalletCode());
                palletDao.deleteByPalletCode(sortPalletDetail.getPalletCode());

                sortPalletDetailHistoryDao.insert(
                        new SortPalletDetailHistory(null, sortPalletDetail.getPalletCode(),
                                sortPalletDetail.getOutboundOrderCode(), sortPalletDetail.getMaterialCode(),
                                sortPalletDetail.getSortNum(), sortPalletDetail.getOldPallet())
                );
                sortPalletDetailDao.deletaById(sortPalletDetail.getId());
            }
        }


        // 单据移入历史表
        OutboundOrder outboundOrder = outboundOrderDao.selectByOrderCode(outboundOrderCode);
        outboundOrder.setStatus(OutboundOrderStatus.COMPLETE);
        outboundOrder.setFinishTime(LocalDateTime.now());
        outboundOrderHistoryDao.insert(
                new OutboundOrderHistory(null, outboundOrder.getOutboundOrderCode(), outboundOrder.getLogisticsCompanyCode(),
                        outboundOrder.getPriority(), outboundOrder.getStatus(), outboundOrder.getDispatchTime(), outboundOrder.getStartTime(),
                        outboundOrder.getFinishTime(), outboundOrder.getNeedStockUp(), outboundOrder.getOrderType())
        );
        outboundOrderDao.deleteById(outboundOrder.getId());

        List<OutboundOrderDetail> outboundOrderDetailList = outboundOrderDetailDao.selectByOutboundOrderCode(outboundOrderCode);
        for (OutboundOrderDetail outboundOrderDetail : outboundOrderDetailList) {
            outboundOrderDetail.setStatus(OutboundDetailStatus.COMPLETE);
            outboundOrderDetailHistoryDao.insert(
                    new OutboundOrderDetailHistory(null, outboundOrderDetail.getOutboundOrderCode(), outboundOrderDetail.getOutboundDetailId(), outboundOrderDetail.getMaterialCode(),
                            outboundOrderDetail.getRequireNum(), outboundOrderDetail.getStockUpNum(), outboundOrderDetail.getCompleteNum(),
                            outboundOrderDetail.getStatus(), outboundOrderDetail.getSuggestLargeStorage(), outboundOrderDetail.getSuggestMediumStorage(),
                            outboundOrderDetail.getSuggestSmallStorage())
            );
            outboundOrderDetailDao.deleteById(outboundOrderDetail.getId());
        }

        return Tuple.tuple(null, true);
    }


    // 清除小储位后，清理中储位物料信息
    private void judgeAndClearMediumStorage(String smallStorageCode) {
        SmallStorage smallStorage = smallStorageDao.selectBySmallStorage(smallStorageCode);
        MediumStorage mediumStorage = mediumStorageDao.selectByMediumStorage(smallStorage.getMediumStorageCode());
        LargeStorage largeStorage = largeStorageDao.selectByLargeStorageCode(mediumStorage.getLargeStorageCode());

        if (StorageType.STORAGE_AREA_5F != largeStorage.getLargeStorageType() &&
                StorageType.STORAGE_AREA_6F != largeStorage.getLargeStorageType()) return;

        List<PalletMediumStorageSearchPo> palletMediumStorageSearchPoList = storageDao.selectPalletOrOutboundFreezePalletMediumStorage(smallStorage.getMediumStorageCode());

        Map<String, Long> collect = palletMediumStorageSearchPoList.stream()
                .map(a -> a.getMaterialCode() + a.getInboundOrderCode() + a.getBatchCode())
                .collect(Collectors.groupingBy(a -> a, Collectors.counting()));


        String mediumMaterial1 = mediumStorage.getMaterialOne() + mediumStorage.getInboundOrderOne() + mediumStorage.getBatchOne();
        String mediumMaterial2 = mediumStorage.getMaterialTwo() + mediumStorage.getInboundOrderTwo() + mediumStorage.getBatchTwo();

        Long num1 = collect.getOrDefault(mediumMaterial1, 0l);
        Long num2 = collect.getOrDefault(mediumMaterial2, 0l);

        if (num1 == 0l) {
            mediumStorageDao.updateBatch1Info(smallStorage.getMediumStorageCode(), null, null, null);
        }

        if (num2 == 0l) {
            mediumStorageDao.updateBatch2Info(smallStorage.getMediumStorageCode(), null, null, null);
        }
    }


    @Transactional
    public Tuple<String> outboundOrderCancel(String outboundOrderCode) {
        OutboundOrder outboundOrder = outboundOrderDao.selectByOrderCode(outboundOrderCode);
        if (outboundOrder.getStatus() != OutboundOrderStatus.INIT) {
            return Tuple.tuple("订单已开启，取消失败", false);
        }


        outboundOrder.setStatus(OutboundOrderStatus.CANCEL);
        outboundOrder.setFinishTime(LocalDateTime.now());
        outboundOrderHistoryDao.insert(
                new OutboundOrderHistory(null, outboundOrder.getOutboundOrderCode(), outboundOrder.getLogisticsCompanyCode(),
                        outboundOrder.getPriority(), outboundOrder.getStatus(), outboundOrder.getDispatchTime(), outboundOrder.getStartTime(),
                        outboundOrder.getFinishTime(), outboundOrder.getNeedStockUp(), outboundOrder.getOrderType())
        );
        outboundOrderDao.deleteById(outboundOrder.getId());

        List<OutboundOrderDetail> outboundOrderDetailList = outboundOrderDetailDao.selectByOutboundOrderCode(outboundOrderCode);
        for (OutboundOrderDetail outboundOrderDetail : outboundOrderDetailList) {
            outboundOrderDetail.setStatus(OutboundDetailStatus.CANCEL);
            outboundOrderDetailHistoryDao.insert(
                    new OutboundOrderDetailHistory(null, outboundOrderDetail.getOutboundOrderCode(), outboundOrderDetail.getOutboundDetailId(), outboundOrderDetail.getMaterialCode(),
                            outboundOrderDetail.getRequireNum(), outboundOrderDetail.getStockUpNum(), outboundOrderDetail.getCompleteNum(),
                            outboundOrderDetail.getStatus(), outboundOrderDetail.getSuggestLargeStorage(), outboundOrderDetail.getSuggestMediumStorage(),
                            outboundOrderDetail.getSuggestSmallStorage())
            );
            outboundOrderDetailDao.deleteById(outboundOrderDetail.getId());
        }

        // 看看有没有备货
        List<WcsMission> wcsMissionList = wcsMissionDao.selectByOrderCodeAndMissionType(outboundOrderCode, MissionType.OUTBOUND_STOCK_UP);
        for (WcsMission wcsMission : wcsMissionList) {
            AgvMission agvMission = agvMissionDao.selectByWcsMissionId(wcsMission.getMissionId());
            agvTaskDispatcher.cancel(agvMission.getTaskId());
        }

        // 待分拣储位找栈板拉回去，outboundFreeze不为空就等待
//mediumStorageDao

        return Tuple.tuple(null, true);
    }


    public Tuple<String> sortException(WmsSortException wmsSortException) {
        String palletCode = wmsSortException.getPalletCode();
        List<OutboundTaskRecord> outboundTaskRecordList = outboundTaskRecordDao.selectSortPalletByPalletCode(
                palletCode,
                OutboundContentPool.OUTBOUND_RECORD_SORT
        );
        OutboundTaskRecord realRecord = null;
        WcsMission wcsMission = null;

        for (OutboundTaskRecord outboundTaskRecord : outboundTaskRecordList) {
            wcsMission = wcsMissionDao.selectWcsMission(
                    outboundTaskRecord.getOutboundOrderCode(),
                    MissionType.OUTBOUND_SORT.name(),
                    OutboundSortMissionStatus.WAIT_FOR_CHECK_EXCEPTION.name(),
                    outboundTaskRecord.getOutboundRecordId()
            );
            if (wcsMission != null) {
                realRecord = outboundTaskRecord;
                break;
            }
        }

        if (realRecord == null) {
            return Tuple.tuple("没找到该栈板信息", false);
        }

        if (wcsMission.getEnumMissionStatus() != OutboundSortMissionStatus.WAIT_FOR_CHECK_EXCEPTION) {
            return Tuple.tuple("车体未到位，请车体到位叫车", false);
        }


        // 修改原栈板任务状态,退回栈板
        wcsMissionDao.updateMissionStatusByMissionId(
                wcsMission.getMissionId(),
                OutboundSortMissionStatus.SORT_EXCEPTION.name()
        );


        return Tuple.tuple(null, true);
    }

}
